package math.base;

/**
 * @author Saga
 *
 */
public class Coord4d {
    public Coord4d() {
        this.x = 0.0;
        this.y = 0.0;
        this.z = 0.0;
        this.w = 0.0;
    }

    public Coord4d(final double _x, final double _y, final double _z, final double _w) {
        this.x = _x;
        this.y = _y;
        this.z = _z;
        this.w = _w;
    }

    public final void init(final double _x, final double _y, final double _z, final double _w) {
        this.x = _x;
        this.y = _y;
        this.z = _z;
        this.w = _w;
    }

    public final void setX(final double _x) {
        this.x = _x;
    }

    public final double getX() {
        return this.x;
    }

    public final void setY(final double _y) {
        this.y = _y;
    }

    public final double getY() {
        return this.y;
    }

    public final void setZ(final double _z) {
        this.z = _z;
    }

    public final double getZ() {
        return this.z;
    }

    public final void setW(final double _w) {
        this.w = _w;
    }

    public final double getW() {
        return this.w;
    }

    public final void setCoord(final int _id, final double _val) {
        switch (_id) {
        case 0:
            this.x = _val;
            return;

        case 1:
            this.y = _val;
            return;

        case 2:
            this.z = _val;
            return;

        case 3:
            this.w = _val;
            return;

        default:
            throw new IndexOutOfBoundsException("");
        }
    }

    public final double getCoord(final int _id) {
        switch (_id) {
        case 0:
            return this.x;

        case 1:
            return this.y;

        case 2:
            return this.z;

        case 3:
            return this.w;

        default:
            throw new IndexOutOfBoundsException("");
        }
    }

    //equal
    public final boolean isEqual(final Coord4d _coord, final double _tol) {
        return Math.abs(this.x - _coord.x) <= _tol && Math.abs(this.y - _coord.y) <= _tol &&
                Math.abs(this.z - _coord.z) <= _tol && Math.abs(this.w - _coord.w) <= _tol;
    }

    //add
    public final void add(final Coord4d _coord) {
        this.x += _coord.x;
        this.y += _coord.y;
        this.z += _coord.z;
        this.w += _coord.w;
    }

    public final Coord4d added(final Coord4d _coord) {
        return new Coord4d(this.x + _coord.x, this.y + _coord.y, this.z + _coord.z,
                this.w + _coord.w);
    }

    //subtract
    public final void subtract(final Coord4d _coord) {
        this.x -= _coord.x;
        this.y -= _coord.y;
        this.z -= _coord.z;
        this.w -= _coord.w;
    }

    public final Coord4d subtracted(final Coord4d _coord) {
        return new Coord4d(this.x - _coord.x, this.y - _coord.y, this.z - _coord.z,
                this.w - _coord.w);
    }

    //reverse
    public final void reverse() {
        this.x = -this.x;
        this.y = -this.y;
        this.z = -this.z;
        this.w = -this.w;
    }

    public final Coord4d reversed() {
        return new Coord4d(-this.x, -this.y, -this.z, -this.w);
    }

    //multiply
    public final void multiply(final double _scalar) {
        this.x *= _scalar;
        this.y *= _scalar;
        this.z *= _scalar;
        this.w *= _scalar;
    }

    public final Coord4d multiplied(final double _scalar) {
        return new Coord4d(this.x * _scalar, this.y * _scalar, this.z * _scalar, this.w * _scalar);
    }

    public final void multiply(final Coord4d _coord) {
        this.x *= _coord.x;
        this.y *= _coord.y;
        this.z *= _coord.z;
        this.w *= _coord.w;
    }

    public final Coord4d multiplied(final Coord4d _coord) {
        return new Coord4d(this.x * _coord.x, this.y * _coord.y, this.z * _coord.z,
                this.w * _coord.w);
    }

    public final void multiply(final Matrix4d _mat) {
        final double tempX = this.x * _mat.getCoord(0, 0) + this.y * _mat.getCoord(1, 0) +
                this.z * _mat.getCoord(2, 0) + this.w * _mat.getCoord(3, 0);
        final double tempY = this.x * _mat.getCoord(0, 1) + this.y * _mat.getCoord(1, 1) +
                this.z * _mat.getCoord(2, 1) + this.w * _mat.getCoord(3, 1);
        final double tempZ = this.x * _mat.getCoord(0, 2) + this.y * _mat.getCoord(1, 2) +
                this.z * _mat.getCoord(2, 2) + this.w * _mat.getCoord(3, 2);
        this.w = this.x * _mat.getCoord(0, 3) + this.y * _mat.getCoord(1, 3) +
                this.z * _mat.getCoord(2, 3) + this.w * _mat.getCoord(3, 3);
        this.x = tempX;
        this.y = tempY;
        this.z = tempZ;
    }

    public final Coord4d multiplied(final Matrix4d _mat) {
        return new Coord4d(this.x * _mat.getCoord(0, 0) + this.y * _mat.getCoord(1, 0) +
                this.z * _mat.getCoord(2, 0) + this.w * _mat.getCoord(3, 0),
                this.x * _mat.getCoord(0, 1) + this.y * _mat.getCoord(1, 1) +
                        this.z * _mat.getCoord(2, 1) + this.w * _mat.getCoord(3, 1),
                this.x * _mat.getCoord(0, 2) + this.y * _mat.getCoord(1, 2) +
                        this.z * _mat.getCoord(2, 2) + this.w * _mat.getCoord(3, 2),
                this.x * _mat.getCoord(0, 3) + this.y * _mat.getCoord(1, 3) +
                        this.z * _mat.getCoord(2, 3) + this.w * _mat.getCoord(3, 3));
    }

    public final void preMultiply(final Matrix4d _mat) {
        final double tempX = this.x * _mat.getCoord(0, 0) + this.y * _mat.getCoord(0, 1) +
                this.z * _mat.getCoord(0, 2) + this.w * _mat.getCoord(0, 3);
        final double tempY = this.x * _mat.getCoord(1, 0) + this.y * _mat.getCoord(1, 1) +
                this.z * _mat.getCoord(1, 2) + this.w * _mat.getCoord(1, 3);
        final double tempZ = this.x * _mat.getCoord(2, 0) + this.y * _mat.getCoord(2, 1) +
                this.z * _mat.getCoord(2, 2) + this.w * _mat.getCoord(2, 3);
        this.w = this.x * _mat.getCoord(3, 0) + this.y * _mat.getCoord(3, 1) +
                this.z * _mat.getCoord(3, 2) + this.w * _mat.getCoord(3, 3);
        this.x = tempX;
        this.y = tempY;
        this.z = tempZ;
    }

    public final Coord4d preMultiplied(final Matrix4d _mat) {
        return new Coord4d(this.x * _mat.getCoord(0, 0) + this.y * _mat.getCoord(0, 1) +
                this.z * _mat.getCoord(0, 2) + this.w * _mat.getCoord(0, 3),
                this.x * _mat.getCoord(1, 0) + this.y * _mat.getCoord(1, 1) +
                        this.z * _mat.getCoord(1, 2) + this.w * _mat.getCoord(1, 3),
                this.x * _mat.getCoord(2, 0) + this.y * _mat.getCoord(2, 1) +
                        this.z * _mat.getCoord(2, 2) + this.w * _mat.getCoord(2, 3),
                this.x * _mat.getCoord(3, 0) + this.y * _mat.getCoord(3, 1) +
                        this.z * _mat.getCoord(3, 2) + this.w * _mat.getCoord(3, 3));
    }

    //divide
    public final void divide(final double _denom) {
        final double scalar = 1.0 / _denom;
        this.x *= scalar;
        this.y *= scalar;
        this.z *= scalar;
        this.w *= scalar;
    }

    public final Coord4d divided(final double _denom) {
        final double scalar = 1.0 / _denom;
        return new Coord4d(this.x * scalar, this.y * scalar, this.z * scalar, this.w * scalar);
    }

    public final void divide(final Coord4d _coord) {
        this.x /= _coord.x;
        this.y /= _coord.y;
        this.z /= _coord.z;
        this.w /= _coord.w;
    }

    public final Coord4d divided(final Coord4d _coord) {
        return new Coord4d(this.x / _coord.x, this.y / _coord.y, this.z / _coord.z,
                this.w / _coord.w);
    }

    //product
    public final double dot(final Coord4d _coord) {
        return this.x * _coord.x + this.y * _coord.y + this.z * _coord.z + this.w * _coord.w;
    }

    //magnitude
    public final double calcMagnitude() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
    }

    public final double calcSquareMagnitude() {
        return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
    }

    //normalize
    void normalize() {
        final double mag = Math.sqrt(
                this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        if (mag <= Precision.confusion) {
            throw new ArithmeticException("");
        }

        this.x /= mag;
        this.y /= mag;
        this.z /= mag;
        this.w /= mag;
    }

    public final Coord4d normalized() {
        final double mag = Math.sqrt(
                this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        if (mag <= Precision.confusion) {
            throw new ArithmeticException("");
        }

        return new Coord4d(this.x / mag, this.y / mag, this.z / mag, this.w / mag);
    }

    @Override
    public Coord4d clone() {
        return new Coord4d(this.x, this.y, this.z, this.w);
    }

    private double x;
    private double y;
    private double z;
    private double w;
}

